package com.tms.security;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 当一个请求走完 FilterInvocationSecurityMetadataSource 中的getAttributes()方法
 * 就会来到这里进行
 * 权限判断-角色信息对比
 *
 * @author ozj
 */
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {

    /**
     * 判断用户是否有当前请求url的权限，不具备则抛出异常，否则不做任何事
     *
     * @param auth   当前用户的登录信息
     * @param object 一个FilterInvocation对象，可以获取当前请求对象
     * @param ca     FilterInvocationSecurityMetadataSource 中的getAttributes() 方法返回值，即当前请求URL所需要的角色
     */
    @Override
    public void decide(Authentication auth, Object object, Collection<ConfigAttribute> ca) {
        Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
        FilterInvocation filterInvocation = (FilterInvocation) object;
        // 放过websocket
        if (filterInvocation.getRequestUrl().startsWith("/socket/")) {
            return;
        }
        // 放过admin端点监控
        if (filterInvocation.getRequestUrl().startsWith("/actuator")) {
            return;
        }
        if (filterInvocation.getRequestUrl().startsWith("/upload/")) {
            return;
        }
        if (filterInvocation.getRequestUrl().startsWith("/model/")) {
            return;
        }
        if (filterInvocation.getRequestUrl().equalsIgnoreCase("/api/users/login")) {
            return;
        }
        for (ConfigAttribute configAttribute : ca) {
            //“ROLE_LOGIN”是当前请求URL用户登录即可访问，auth是UsernamePasswordAuthenticationToken的一个实例，说明用户已登录
            if ("ROLE_LOGIN".equals(configAttribute.getAttribute())
                    && auth instanceof UsernamePasswordAuthenticationToken) {
                return;
            }
            //判断用户是否具备当前请求所需要的角色，有则方法结束
            for (GrantedAuthority authority : auths) {
                if (configAttribute.getAttribute().equals(authority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}
